import fitz  # PyMuPDF
from PyQt5.QtWidgets import (
    QGraphicsScene, QGraphicsView, QGraphicsRectItem, QGraphicsPixmapItem,
    QWidget, QVBoxLayout, QLabel, QSlider, QPushButton, QComboBox, QFileDialog, QHBoxLayout
)
from PyQt5.QtGui import QPen, QBrush, QColor, QPixmap, QImage, QPainter
from PyQt5.QtCore import QRectF, Qt, QSize, QRect
from PyQt5.QtSvg import QSvgGenerator
import logging
import random
import os
import tempfile
import pytesseract
from PIL import Image
import numpy as np

class PDFSquareVisualizer:
    def __init__(self):
        # 创建主窗口容器
        self.main_widget = QWidget()
        self.main_layout = QVBoxLayout(self.main_widget)
        
        # 创建场景和视图
        self.scene = QGraphicsScene()
        self.view = QGraphicsView(self.scene)
        self.view.setWindowTitle("PDF文字方块可视化")
        self.view.setAttribute(Qt.WA_DeleteOnClose, False)
        self.view.setRenderHint(QPainter.Antialiasing)
        self.view.setRenderHint(QPainter.SmoothPixmapTransform)
        self.view.setDragMode(QGraphicsView.ScrollHandDrag)
        self.view.setTransformationAnchor(QGraphicsView.AnchorUnderMouse)
        self.view.setResizeAnchor(QGraphicsView.AnchorUnderMouse)
        self.view.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
        self.view.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
        
        # 设置Tesseract路径
        if os.name == 'nt':
            pytesseract.pytesseract.tesseract_cmd = r'C:\Program Files\Tesseract-OCR\tesseract.exe'
        
        # 创建控制面板
        self.control_panel = self._create_control_panel()
        
        # 将控制面板和视图添加到主布局
        self.main_layout.addWidget(self.control_panel)
        self.main_layout.addWidget(self.view)
        
        # 设置主窗口大小
        self.main_widget.resize(800, 700)
    
    def _create_control_panel(self):
        """创建功能丰富的控制面板"""
        panel = QWidget()
        main_layout = QVBoxLayout()
        
        # 添加页面导航控件 (水平布局)
        nav_layout = QHBoxLayout()
        self.page_label = QLabel("页面:")
        self.page_combo = QComboBox()
        self.prev_btn = QPushButton("上一页")
        self.next_btn = QPushButton("下一页")
        
        nav_layout.addWidget(self.page_label)
        nav_layout.addWidget(self.page_combo)
        nav_layout.addWidget(self.prev_btn)
        nav_layout.addWidget(self.next_btn)
        nav_layout.addStretch()
        
        self.prev_btn.clicked.connect(self._prev_page)
        self.next_btn.clicked.connect(self._next_page)
        self.page_combo.currentIndexChanged.connect(self._page_selected)
        
        # 修改可视化模式控件 (水平布局)
        mode_layout = QHBoxLayout()
        mode_label = QLabel("可视化模式:")
        self.mode_combo = QComboBox()
        # 移除扫描增强模式
        self.mode_combo.addItems(["字符方块", "文本行方块", "段落方块"])
        
        mode_layout.addWidget(mode_label)
        mode_layout.addWidget(self.mode_combo)
        mode_layout.addStretch()
        
        self.mode_combo.currentIndexChanged.connect(self._change_visualization_mode)
        
        # 添加显示控制按钮 (水平布局)
        display_layout = QHBoxLayout()
        
        # 透明度控制
        opacity_label = QLabel("方块透明度:")
        self.opacity_slider = QSlider(Qt.Horizontal)
        self.opacity_slider.setRange(10, 90)
        self.opacity_slider.setValue(60)
        
        # 颜色控制
        color_scheme_label = QLabel("配色方案:")
        self.color_scheme_combo = QComboBox()
        self.color_scheme_combo.addItems(["彩虹色", "单色渐变", "对比色", "随机色"])
        
        display_layout.addWidget(opacity_label)
        display_layout.addWidget(self.opacity_slider)
        display_layout.addWidget(color_scheme_label)
        display_layout.addWidget(self.color_scheme_combo)
        
        self.opacity_slider.valueChanged.connect(self._update_block_opacity)
        self.color_scheme_combo.currentIndexChanged.connect(self._update_color_scheme)
        
        # 添加功能按钮 (水平布局)
        btn_layout = QHBoxLayout()
        
        # 显示/隐藏背景
        self.toggle_bg_btn = QPushButton("显示/隐藏背景")
        self.toggle_bg_btn.setCheckable(True)
        self.toggle_bg_btn.setChecked(True)
        
        # 重置视图
        self.reset_view_btn = QPushButton("重置视图")
        
        # 导出按钮
        self.export_btn = QPushButton("导出为图像")
        
        # 放缩按钮
        self.zoom_in_btn = QPushButton("放大")
        self.zoom_out_btn = QPushButton("缩小")
        
        btn_layout.addWidget(self.toggle_bg_btn)
        btn_layout.addWidget(self.reset_view_btn)
        btn_layout.addWidget(self.export_btn)
        btn_layout.addWidget(self.zoom_in_btn)
        btn_layout.addWidget(self.zoom_out_btn)
        
        self.toggle_bg_btn.clicked.connect(self._toggle_background)
        self.reset_view_btn.clicked.connect(self._reset_view)
        self.export_btn.clicked.connect(self._export_visualization)
        self.zoom_in_btn.clicked.connect(lambda: self._zoom(1.2))
        self.zoom_out_btn.clicked.connect(lambda: self._zoom(1/1.2))
        
        # 添加状态信息标签
        self.status_label = QLabel("状态: 就绪")
        
        # 将所有布局添加到主布局
        main_layout.addLayout(nav_layout)
        main_layout.addLayout(mode_layout)
        main_layout.addLayout(display_layout)
        main_layout.addLayout(btn_layout)
        main_layout.addWidget(self.status_label)
        
        panel.setLayout(main_layout)
        return panel
    
    # 添加新的交互处理方法
    def _update_block_opacity(self, value):
        """更新所有方块的透明度"""
        opacity = value / 100.0
        for item in self.scene.items():
            if isinstance(item, QGraphicsRectItem):
                color = item.brush().color()
                color.setAlpha(int(opacity * 255))
                item.setBrush(QBrush(color))
    
    def _toggle_background(self, checked):
        """切换背景显示状态"""
        bg_found = False
        for item in self.scene.items():
            if isinstance(item, QGraphicsPixmapItem):  # 找到背景图像
                item.setVisible(checked)
                bg_found = True
        
        if not bg_found:
            self.status_label.setText("未找到背景图像")
    
    def _change_visualization_mode(self, index):
        """切换可视化模式"""
        if hasattr(self, 'current_page'):
            self.status_label.setText(f"切换到{'字符方块' if index==0 else '文本行方块' if index==1 else '段落方块'}可视化模式...")
            self.visualize_current_page()  # 重新生成可视化
        else:
            self.status_label.setText("请先加载PDF文档")
    
    def _update_color_scheme(self, index):
        """更新方块颜色方案"""
        schemes = ["彩虹色", "单色渐变", "对比色", "随机色"]
        selected = schemes[index]
        self.status_label.setText(f"状态: 应用{selected}配色方案...")
        
        # 根据当前视图中的方块重新应用颜色
        items = [item for item in self.scene.items() if isinstance(item, QGraphicsRectItem)]
        
        if selected == "彩虹色":
            # 使用HSV色彩空间生成彩虹色
            import colorsys
            for i, item in enumerate(items):
                h = (i / len(items)) % 1.0
                s = 0.7
                v = 0.9
                r, g, b = colorsys.hsv_to_rgb(h, s, v)
                color = QColor(int(r * 255), int(g * 255), int(b * 255), 180)
                item.setBrush(QBrush(color))
        
        elif selected == "单色渐变":
            # 使用单色的不同明度
            base_color = QColor(50, 100, 200)  # 基础蓝色
            for i, item in enumerate(items):
                value = 100 + (i * 155 // len(items))  # 从较暗到较亮
                color = QColor(base_color)
                color.setAlpha(180)
                color.setHsv(color.hue(), color.saturation(), value)
                item.setBrush(QBrush(color))
        
        elif selected == "对比色":
            # 使用互补色
            colors = [
                QColor(240, 100, 100, 180),  # 红色
                QColor(100, 240, 100, 180),  # 绿色
                QColor(100, 100, 240, 180),  # 蓝色
                QColor(240, 240, 100, 180),  # 黄色
            ]
            for i, item in enumerate(items):
                color = colors[i % len(colors)]
                item.setBrush(QBrush(color))
        
        else:  # 随机色
            for item in items:
                color = QColor(
                    random.randint(100, 240),
                    random.randint(100, 240),
                    random.randint(100, 240),
                    180
                )
                item.setBrush(QBrush(color))
        
        self.status_label.setText(f"状态: {selected}配色方案已应用")
    
    def _reset_view(self):
        """重置视图缩放和位置"""
        self.view.resetTransform()
        self.view.fitInView(self.scene.sceneRect(), Qt.KeepAspectRatio)
    
    def _zoom(self, factor):
        """缩放视图"""
        self.view.scale(factor, factor)
    
    def _prev_page(self):
        """切换到上一页"""
        if hasattr(self, 'current_doc') and hasattr(self, 'current_page'):
            if self.current_page > 0:
                self.current_page -= 1
                self.visualize_current_page()
                self.page_combo.setCurrentIndex(self.current_page)
    
    def _next_page(self):
        """切换到下一页"""
        if hasattr(self, 'current_doc') and hasattr(self, 'current_page'):
            if self.current_page < len(self.current_doc) - 1:
                self.current_page += 1
                self.visualize_current_page()
                self.page_combo.setCurrentIndex(self.current_page)
    
    def _page_selected(self, index):
        """当用户从下拉列表选择页面时"""
        if hasattr(self, 'current_doc') and index != self.current_page:
            self.current_page = index
            self.visualize_current_page()
    
    def visualize_current_page(self):
        """可视化当前页面"""
        self.status_label.setText(f"状态: 正在处理第 {self.current_page + 1} 页...")
        self.scene.clear()
        
        try:
            if not hasattr(self, 'current_doc') or not self.current_doc:
                raise ValueError("没有加载PDF文档")
            
            # 获取当前页面
            page = self.current_doc[self.current_page]
            
            # 获取页面尺寸
            page_width = page.rect.width
            page_height = page.rect.height
            
            # 设置场景大小
            self.scene.setSceneRect(0, 0, page_width, page_height)
            
            # 获取页面上的文字块
            text_blocks = page.get_text("dict", flags=11)["blocks"]
            
            # 添加页面背景作为参考
            background = None
            try:
                # 将页面渲染为图像
                pix = page.get_pixmap(matrix=fitz.Matrix(2, 2), alpha=False)  # 使用更高分辨率
                if pix.samples:
                    # 创建图像用于显示和OCR
                    img_data = bytes(pix.samples)
                    img = QImage(img_data, pix.width, pix.height, pix.stride, QImage.Format_RGB888)
                    
                    # 缩放到页面大小
                    scaled_img = img.scaled(
                        int(page_width), 
                        int(page_height),
                        Qt.IgnoreAspectRatio, 
                        Qt.SmoothTransformation
                    )
                    
                    # 创建背景（半透明）
                    background = QGraphicsPixmapItem(QPixmap.fromImage(scaled_img))
                    background.setOpacity(0.1)  # 非常透明的背景
                    self.scene.addItem(background)
            except Exception as bg_error:
                logging.warning(f"无法添加页面背景: {str(bg_error)}")
            
            # 获取当前可视化模式
            vis_mode = self.mode_combo.currentIndex()
            
            # 保存OCR处理结果
            ocr_blocks = []
            
            # 根据可视化模式选择不同的可视化方法
            if vis_mode == 0:  # 字符方块
                # 优先使用PDF内嵌文本，如果没有则使用OCR结果
                if text_blocks and any("lines" in block for block in text_blocks):
                    self.status_label.setText("使用PDF内嵌文本进行可视化")
                    self.visualize_text_blocks(text_blocks)
                elif ocr_blocks:
                    self.status_label.setText("使用OCR文本进行可视化")
                    self.visualize_ocr_blocks(ocr_blocks)
                else:
                    self.status_label.setText("未找到文本，使用网格布局")
                    self.create_grid_visualization(page_width, page_height)
            
            elif vis_mode == 1:  # 文本行方块
                # TODO: 实现行级可视化
                self.status_label.setText("文本行方块可视化 (未实现)")
                
            elif vis_mode == 2:  # 段落方块
                # TODO: 实现段落级可视化
                self.status_label.setText("段落方块可视化 (未实现)")
            
            # 调整视图
            self._reset_view()
            
            self.status_label.setText(f"状态: 已显示第 {self.current_page + 1} 页")
            
        except Exception as e:
            import traceback
            error_msg = f"可视化当前页面失败: {str(e)}"
            logging.error(f"{error_msg}\n{traceback.format_exc()}")
            self.status_label.setText(error_msg)
            self.scene.addText(f"可视化失败: {str(e)}")
    
    def visualize_pdf(self, pdf_path, page_number=0):
        """将PDF页面可视化为方块"""
        self.scene.clear()
        
        try:
            # 打开PDF文档并保存为成员变量
            self.current_doc = fitz.open(pdf_path)
            self.current_path = pdf_path
            self.current_page = page_number
            
            if self.current_doc.page_count == 0:
                raise ValueError("PDF文档不包含任何页面")
            
            # 初始化页面选择下拉框
            self.page_combo.clear()
            for i in range(self.current_doc.page_count):
                self.page_combo.addItem(f"第 {i+1} 页")
            
            # 设置当前页面
            if page_number >= self.current_doc.page_count:
                page_number = 0
                self.current_page = 0
            
            self.page_combo.setCurrentIndex(page_number)
            
            # 执行当前页面的可视化
            self.visualize_current_page()
            
            # 显示主窗口而不是单独的视图
            self.main_widget.show()
            
        except Exception as e:
            import traceback
            logging.error(f"PDF可视化失败: {str(e)}\n{traceback.format_exc()}")
            self.status_label.setText(f"状态: 可视化失败 - {str(e)}")
            self.scene.addText(f"可视化失败: {str(e)}")
    
    def _export_visualization(self):
        """导出当前可视化为图像"""
        try:
            # 获取场景尺寸并添加一些边距
            scene_rect = self.scene.sceneRect()
            width = int(scene_rect.width())
            height = int(scene_rect.height())
            
            if width <= 0 or height <= 0:
                self.status_label.setText("错误: 场景尺寸无效")
                return
                
            # 创建场景的图像
            image = QImage(width, height, QImage.Format_ARGB32)
            image.fill(Qt.white)  # 白色背景
            
            # 创建并配置绘图器
            painter = QPainter()
            if not painter.begin(image):
                self.status_label.setText("错误: 无法初始化绘图器")
                return
                
            try:
                # 设置高质量渲染
                painter.setRenderHint(QPainter.Antialiasing)
                painter.setRenderHint(QPainter.SmoothPixmapTransform)
                
                # 渲染场景
                self.scene.render(painter, QRectF(0, 0, width, height), scene_rect)
            finally:
                # 确保画家被正确结束
                painter.end()
            
            # 让用户选择保存位置
            file_path, selected_filter = QFileDialog.getSaveFileName(
                self.main_widget, 
                "导出可视化", 
                "", 
                "PNG图像 (*.png);;JPEG图像 (*.jpg);;SVG图像 (*.svg)"
            )
            
            if not file_path:
                return
                
            # 根据选择的文件类型处理
            if file_path.lower().endswith('.svg'):
                # 导出为SVG
                svg_generator = QSvgGenerator()
                svg_generator.setFileName(file_path)
                svg_generator.setSize(QSize(width, height))
                svg_generator.setViewBox(QRect(0, 0, width, height))
                
                svg_painter = QPainter()
                success = svg_painter.begin(svg_generator)
                if success:
                    try:
                        svg_painter.setRenderHint(QPainter.Antialiasing)
                        self.scene.render(svg_painter)
                    finally:
                        svg_painter.end()
                    self.status_label.setText(f"已导出SVG文件到: {file_path}")
                else:
                    self.status_label.setText("导出SVG失败: 无法初始化绘图器")
            else:
                # 添加文件扩展名（如果需要）
                if not file_path.lower().endswith(('.png', '.jpg', '.jpeg')):
                    if "PNG" in selected_filter:
                        file_path += ".png"
                    else:
                        file_path += ".jpg"
                
                # 保存为位图
                if image.save(file_path):
                    self.status_label.setText(f"已导出图像到: {file_path}")
                else:
                    self.status_label.setText(f"保存图像失败: {file_path}")
                    
        except Exception as e:
            import traceback
            error_msg = f"导出过程中出错: {str(e)}"
            logging.error(f"{error_msg}\n{traceback.format_exc()}")
            self.status_label.setText(error_msg)
    
    def visualize_text_blocks(self, text_blocks):
        """可视化PDF中的文本块，更精确地匹配字符大小"""
        for block in text_blocks:
            if "lines" not in block:
                continue
            
            # 给每个文本块一个随机颜色
            block_color = QColor(
                random.randint(100, 240),
                random.randint(100, 240),
                random.randint(100, 240),
                180  # 半透明
            )
            
            for line in block["lines"]:
                for span in line["spans"]:
                    text = span["text"]
                    if not text.strip():  # 跳过空白文本
                        continue
                        
                    bbox = span["bbox"]  # (x0, y0, x1, y1)
                    x0, y0, x1, y1 = bbox
                    width = x1 - x0
                    height = y1 - y0
                    
                    # 获取字体信息
                    font_size = span.get("size", height)  # 使用高度作为默认值
                    font_name = span.get("font", "unknown")
                    font_flags = span.get("flags", 0)
                    
                    # 判断是否为等宽字体
                    is_monospaced = "mono" in font_name.lower() or (font_flags & 1)  # 位标志1通常表示等宽
                    
                    if len(text) > 1 and not is_monospaced:
                        # 非等宽字体需要考虑每个字符的宽度差异
                        # 使用启发式方法分配宽度
                        
                        # 常见字符的相对宽度系数（根据经验值）
                        char_widths = self.analyze_font_metrics(self.current_doc, font_name)
                        
                        # 计算标准宽度
                        standard_width = width / len(text)
                        
                        # 中文、日文、韩文字符通常是等宽的
                        for char in text:
                            if ord(char) > 0x4E00:  # CJK字符范围开始
                                char_widths[char] = 1.0
                        
                        # 计算总权重和调整因子
                        total_factor = sum(char_widths.get(c, 1.0) for c in text)
                        adjustment = width / (standard_width * total_factor)
                        
                        # 绘制每个字符的方块
                        current_x = x0
                        for char in text:
                            if char.strip():  # 跳过空白字符
                                factor = char_widths.get(char, 1.0)
                                char_width = standard_width * factor * adjustment
                                
                                rect_item = QGraphicsRectItem(
                                    QRectF(current_x, y0, char_width, height)
                                )
                                rect_item.setPen(QPen(QColor(0, 0, 0, 100)))
                                rect_item.setBrush(QBrush(block_color))
                                self.scene.addItem(rect_item)
                                
                                # 更新X坐标
                                current_x += char_width
                            else:
                                # 空白字符
                                current_x += standard_width * char_widths.get(' ', 0.6) * adjustment
                    else:
                        # 等宽字体或单字符，直接使用等宽方块
                        if len(text) > 1:
                            char_width = width / len(text)
                            for i in range(len(text)):
                                if text[i].strip():  # 跳过空白字符
                                    rect_item = QGraphicsRectItem(
                                        QRectF(x0 + i * char_width, y0, char_width, height)
                                    )
                                    rect_item.setPen(QPen(QColor(0, 0, 0, 100)))
                                    rect_item.setBrush(QBrush(block_color))
                                    self.scene.addItem(rect_item)
                        else:
                            # 单字符
                            rect_item = QGraphicsRectItem(QRectF(x0, y0, width, height))
                            rect_item.setPen(QPen(QColor(0, 0, 0, 100)))
                            rect_item.setBrush(QBrush(block_color))
                            self.scene.addItem(rect_item)
    
    def analyze_font_metrics(self, doc, font_name):
        """分析字体特性，获取字符宽度信息"""
        # 缓存分析结果避免重复计算
        if not hasattr(self, 'font_metrics'):
            self.font_metrics = {}
        
        # 如果已经分析过此字体，直接返回结果
        if font_name in self.font_metrics:
            return self.font_metrics[font_name]
        
        # 默认字符宽度比例
        char_widths = {
            'i': 0.5, 'l': 0.5, 'I': 0.5, 'j': 0.5, 't': 0.6, 'f': 0.6, 'r': 0.7,
            's': 0.8, 'z': 0.8, 'a': 0.9, 'c': 0.9, 'e': 0.9, 'o': 0.9, 'n': 0.9,
            'u': 0.9, 'v': 0.9, 'x': 0.9, 'y': 0.9, 'h': 0.9, 'k': 0.9, 'b': 0.9,
            'd': 0.9, 'g': 0.9, 'p': 0.9, 'q': 0.9, 'm': 1.4, 'w': 1.4, 'W': 1.8,
            'M': 1.6, ' ': 0.6, '.': 0.5, ',': 0.5, ':': 0.5, ';': 0.5
        }
        
        try:
            # 判断字体是否是等宽字体
            is_monospaced = "mono" in font_name.lower()
            
            # 如果字体是等宽的，所有字符宽度相同
            if is_monospaced:
                for char in char_widths:
                    char_widths[char] = 1.0
        except:
            # 分析失败时使用默认值
            pass
        
        # 保存结果到缓存
        self.font_metrics[font_name] = char_widths
        return char_widths
    
    def visualize_ocr_blocks(self, ocr_blocks):
        """可视化OCR识别的文本块，优化中文处理"""
        # 检查是否可能是中文文档
        is_chinese_doc = False
        if hasattr(self, 'current_path'):
            doc_name = os.path.basename(self.current_path).lower()
            if any(char in doc_name for char in "中文汉字北京上海广州"):
                is_chinese_doc = True
            # 也可以检查OCR文本内容
            if not is_chinese_doc:
                chinese_chars = 0
                for block in ocr_blocks:
                    for char in block['text']:
                        if ord(char) > 0x4E00:
                            chinese_chars += 1
                # 如果中文字符超过一定数量，认为是中文文档
                if chinese_chars > 10:  
                    is_chinese_doc = True
        
        # 改进行分组算法 - 特别是对中文文档
        # 简单的聚类 - 将相近的y坐标分到同一组
        rows = {}
        current_row = 0
        last_y = -1
        
        # 对于中文文档，使用更小的行间距阈值
        y_threshold = 3 if is_chinese_doc else 5  
        
        # 按y坐标排序
        sorted_blocks = sorted(ocr_blocks, key=lambda b: b['bbox'][1])
        
        for block in sorted_blocks:
            y = block['bbox'][1]
            # 如果与上一个坐标相距较远，开始新行
            if last_y == -1 or (y - last_y) > y_threshold:
                current_row += 1
            
            if current_row not in rows:
                rows[current_row] = []
            
            rows[current_row].append(block)
            last_y = y
        
        # 为每行分配一种颜色
        n_rows = len(rows)
        for idx, (row_key, blocks) in enumerate(rows.items()):
            # 使用HSV色彩空间生成和谐的颜色
            hue = (idx / n_rows) * 360
            row_color = QColor()
            row_color.setHsv(int(hue), 200, 220, 180)
            
            # 对于中文文档，需要特别处理
            if is_chinese_doc:
                # 计算这一行的平均高度和宽度
                heights = [block['bbox'][3] - block['bbox'][1] for block in blocks]
                avg_height = sum(heights) / len(heights) if heights else 0
                
                # 首先收集这一行的所有字符
                all_chars = []
                for block in blocks:
                    all_chars.extend(block['text'])
                
                # 计算中文字符数量
                cjk_chars = [c for c in all_chars if ord(c) > 0x4E00]
                cjk_count = len(cjk_chars)
                
                # 确定这一行应该使用的统一方块大小
                if cjk_count > 0:  # 如果存在中文字符
                    # 确保中文字符是方形的
                    standard_size = avg_height
                    
                    # 空格和标点符号的特殊颜色
                    space_color = QColor(row_color)
                    space_color.setAlpha(100)  # 更透明
                    
                    punct_color = QColor(row_color)
                    punct_color.setHsv((int(hue) + 30) % 360, 150, 220, 180)  # 略微不同的色调
                
                # 处理这一行的每个块
                for block in blocks:
                    text = block['text']
                    x0, y0, x1, y1 = block['bbox']
                    width = x1 - x0
                    height = y1 - y0
                    
                    # 检测是否包含中文字符
                    has_cjk = any(ord(c) > 0x4E00 for c in text)
                    
                    if has_cjk or is_chinese_doc:  # 中文块或中文文档
                        # 每个字符使用相同的方块大小，按不同类型微调
                        char_count = len(text)
                        
                        # 计算初始位置
                        current_x = x0
                        
                        # 为每个字符创建方块
                        for char in text:
                            is_cjk = ord(char) > 0x4E00
                            is_punct = char in '，。！？；：""''（）【】《》、'
                            is_digit = char.isdigit()
                            is_space = char.isspace()
                            
                            # 设置不同类型字符的大小
                            if is_cjk:  # 中文字符 - 方形
                                char_width = standard_size
                                char_height = standard_size
                                char_color = row_color
                            elif is_digit:  # 数字 - 窄一些
                                char_width = standard_size * 0.6
                                char_height = standard_size
                                char_color = row_color
                            elif is_punct:  # 标点 - 小一些
                                char_width = standard_size * 0.5
                                char_height = standard_size * 0.8
                                char_color = punct_color
                            elif is_space:  # 空格
                                char_width = standard_size * 0.5
                                char_height = standard_size
                                char_color = space_color
                            else:  # 其他字符（如英文）
                                char_width = standard_size * 0.7
                                char_height = standard_size
                                char_color = row_color
                            
                            # 垂直居中
                            y_offset = (height - char_height) / 2 if height > char_height else 0
                            
                            # 创建方块
                            rect_item = QGraphicsRectItem(
                                QRectF(current_x, y0 + y_offset, char_width, char_height)
                            )
                            rect_item.setPen(QPen(QColor(0, 0, 0, 100)))
                            rect_item.setBrush(QBrush(char_color))
                            self.scene.addItem(rect_item)
                            
                            # 更新下一个字符的位置
                            current_x += char_width
                    else:
                        # 非中文块的处理...（保持原有代码）
                        pass
    
    def create_grid_visualization(self, width, height):
        """创建更逼真的文档布局模拟"""
        # 添加一些文档分析，检测可能的列数
        columns = 1  # 默认单列
        
        # 猜测文档类型
        if width / height > 1.3:  # 宽屏比例，可能是幻灯片
            self._create_presentation_layout(width, height)
        elif width / height < 0.8:  # 可能是长文档
            if random.random() > 0.7:  # 30%概率是双列布局
                columns = 2
            self._create_document_layout(width, height, columns)
        else:  # 标准A4比例
            if random.random() > 0.6:  # 40%概率是双列
                columns = 2
            self._create_document_layout(width, height, columns)

    def _create_document_layout(self, width, height, columns=1):
        """创建逼真的文档布局"""
        # 页边距
        margin_top = height * 0.05
        margin_bottom = height * 0.05
        margin_left = width * 0.08
        margin_right = width * 0.08
        
        # 可用区域
        usable_width = width - margin_left - margin_right
        usable_height = height - margin_top - margin_bottom
        
        # 多列布局
        column_width = usable_width / columns
        column_gap = column_width * 0.1 if columns > 1 else 0
        
        # 绘制页眉（可选）
        if random.random() > 0.3:  # 70%的概率有页眉
            header_height = height * 0.03
            header_y = margin_top / 2 - header_height / 2
            
            # 生成页眉方块
            self._add_colored_rect(
                margin_left, 
                header_y, 
                usable_width * random.uniform(0.3, 0.7), 
                header_height
            )
        
        # 对每一列生成内容
        for col in range(columns):
            col_x = margin_left + col * (column_width + column_gap)
            col_width = column_width - column_gap
            
            # 当前Y位置
            current_y = margin_top
            
            # 标题（只在第一列）
            if col == 0:
                title_height = height * 0.04
                self._add_colored_rect(col_x, current_y, col_width * 0.8, title_height)
                current_y += title_height * 1.5
            
            # 副标题（可选）
            if col == 0 and random.random() > 0.5:  # 50%的概率有副标题
                subtitle_height = height * 0.025
                self._add_colored_rect(col_x, current_y, col_width * 0.6, subtitle_height)
                current_y += subtitle_height * 1.5
            
            # 生成段落
            while current_y < margin_top + usable_height - height * 0.04:
                # 段落高度（行数 × 行高）
                lines_in_para = random.randint(3, 8)
                line_height = height * 0.018
                para_height = lines_in_para * line_height * 1.2  # 1.2为行间距
                
                # 确保不超出底部边界
                if current_y + para_height > margin_top + usable_height:
                    break
                
                # 生成段落中的每一行
                for i in range(lines_in_para):
                    # 行宽（最后一行通常较短）
                    if i == lines_in_para - 1 and random.random() > 0.3:  # 70%的概率最后一行较短
                        line_width = col_width * random.uniform(0.5, 0.8)